#include "Scheduler.h"
#include "Settings.h"

///Copyright (c) 2009-2010 T. Klepzig 

map<HWND, Scheduler*> Scheduler::instances;

Scheduler::Scheduler(HINSTANCE hInstance)
{
    this->hInstance = hInstance;
    dialogOpen = false;

    Settings settings;
    Settings::Data data = {0};
    settings.Load(&data);
    alert = data.SchedulerData.Alert;

    LPCTSTR className = TEXT("SchedulerHiddenWndClass");

    WNDCLASSEX wndClass;
    ZeroMemory(&wndClass, sizeof(wndClass));
    wndClass.cbSize=sizeof(wndClass);

    wndClass.lpfnWndProc=SchedulerHiddenWindowWndProc;
    wndClass.hInstance=hInstance;
    wndClass.hCursor=LoadCursor(0,IDC_ARROW);
    wndClass.hbrBackground=GetStockBrush(BLACK_BRUSH);
    wndClass.lpszClassName=className;

    RegisterClassEx(&wndClass);

    hiddenWindow=CreateWindowEx(0,className,0,WS_POPUPWINDOW,0,0,100,100,0,0,hInstance,0);

    instances[hiddenWindow] = this;
}

Scheduler::~Scheduler()
{
    Deactivate();
    DestroyWindow(hiddenWindow);
    instances.erase(hiddenWindow);
}


CCState Scheduler::Activate()
{		
    CCState result;
    if ((result = CassiopeiaComponent::Activate()) != CCState::Successful)
        return result;

    RegisterHotKey(hiddenWindow,1,MOD_WIN,83);
    SetTimer(hiddenWindow,34,200,TimerProc);

    return CCState::Successful;
}

CCState Scheduler::Deactivate()
{
    CCState result;
    if ((result = CassiopeiaComponent::Deactivate()) != CCState::Successful)
        return result;
    
    UnregisterHotKey(hiddenWindow,1);
    KillTimer(hiddenWindow,34);
    PlaySound(0,0,0);

    return CCState::Successful;
}

void Scheduler::SetAlert()
{
    if (dialogOpen || !active)
        return;

    dialogOpen=true;
    DialogBoxParam(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), 0, DialogProc, (LPARAM)this);
    dialogOpen=false;
}


void Scheduler::ShowAlertWindow(LPCTSTR text)
{
    POINT pos;
    GetCursorPos(&pos);

    HMONITOR monitor = MonitorFromPoint(pos,MONITOR_DEFAULTTOPRIMARY);
    MONITORINFO mi;
    mi.cbSize = sizeof(MONITORINFO);

    GetMonitorInfo(monitor,&mi);

    int screenWidth = mi.rcWork.right-mi.rcWork.left;
    int screenHeight = mi.rcWork.bottom-mi.rcWork.top;

    int width = screenWidth * 0.8;
    int height = 100;

    int x = mi.rcWork.left + screenWidth/2-width/2;
    int y = mi.rcWork.top + screenHeight/2-height/2;

    LPCTSTR className = TEXT("SchedulerAlertWndClass");

    WNDCLASSEX wndClass;
    ZeroMemory(&wndClass, sizeof(wndClass));
    wndClass.cbSize=sizeof(wndClass);

    wndClass.lpfnWndProc=SchedulerAlertWindowWndProc;
    wndClass.hInstance=hInstance;
    wndClass.hCursor=LoadCursor(0,IDC_ARROW);
    wndClass.hbrBackground=GetStockBrush(BLACK_BRUSH);
    wndClass.lpszClassName=className;

    RegisterClassEx(&wndClass);

    HWND hWnd=CreateWindowEx(WS_EX_DLGMODALFRAME|WS_EX_TOPMOST,className,text,WS_CAPTION|WS_SYSMENU,x,y,width,height,0,0,hInstance,&alert.Text);

    HICON icon = LoadIcon(hInstance,MAKEINTRESOURCE(IDI_ICON1));
    SendMessage(hWnd,WM_SETICON,ICON_SMALL,(LPARAM)icon);
    SendMessage(hWnd,WM_SETICON,ICON_BIG,(LPARAM)icon);

    ShowWindow(hWnd,SW_SHOW);
    UpdateWindow(hWnd);	
    
    MARGINS margins = {-1,-1,-1,-1};
    DwmExtendFrameIntoClientArea(hWnd,&margins);

}

void Scheduler::CreateAlert(HWND hWndDlg)
{
    //New Alert
    SYSTEMTIME time;
    DateTime_GetSystemtime(GetDlgItem(hWndDlg,IDC_DATETIMEPICKER1),&time);

    AlertTimeInfo ti={0};
    ti.Active = true;

    if (Button_GetCheck(GetDlgItem(hWndDlg,IDC_CHECK_SOUND)) == BST_CHECKED)
        ti.Sound = true;
    else
        ti.Sound = false;


    TCHAR soundFile[MAX_PATH]={0};
    GetDlgItemText(hWndDlg,IDC_EDIT_SOUNDFILE,soundFile,sizeof(soundFile));

    TCHAR command[MAX_PATH]={0};
    GetDlgItemText(hWndDlg,IDC_EDIT_COMMAND,command,sizeof(command));

    TCHAR commandArgs[MAX_PATH]={0};
    GetDlgItemText(hWndDlg,IDC_EDIT_ARGS,commandArgs,sizeof(commandArgs));

    TCHAR text[MAX_PATH]={0};
    GetDlgItemText(hWndDlg,IDC_EDIT_TEXT,text,sizeof(text));

    _tcscpy(ti.Command,command);
    _tcscpy(ti.Text,text);
    _tcscpy(ti.CommandArgs,commandArgs);
    _tcscpy(ti.SoundFile,soundFile);

    ti.Time = time;
    ti.time_t = ConvertSystemTimeToTime_t(time);

    Settings settings;
    Settings::Data data = {0};
    settings.Load(&data);
    data.SchedulerData.Alert = ti;
    _tcscpy(data.SchedulerData.LastSoundFile,ti.SoundFile);
    data.SchedulerData.LastSoundActive = ti.Sound;

    if (!settings.Save(data))
    {
        if (MessageBox(0,TEXT("Saving Alert failed!\nContinue without saving?"),TEXT("Error"),MB_YESNO) == IDNO)
            return;
    }

    alert = ti;
}

void Scheduler::CheckAlert()
{
    time_t now=time(0);

    tm timeInfo;
    localtime_s(&timeInfo,&now);

    //solve the DST problem
    if (timeInfo.tm_isdst > 0)
    {
        timeInfo.tm_hour++;
        now = mktime(&timeInfo);
    }

    double diff = -1;

    diff=difftime(now,alert.time_t);
    
    if (alert.Active && diff >=0)
    {		
        alert.Active=false;

        Settings settings;
        Settings::Data data = {0};
        settings.Load(&data);
        data.SchedulerData.Alert.Active = false;
        settings.Save(data);
        
        
        if (alert.Sound && _tcslen(alert.SoundFile) > 0)
            PlaySound(alert.SoundFile,hInstance,SND_FILENAME|SND_ASYNC|SND_LOOP);
        
        if (_tcslen(alert.Command) > 0)
        {
            TCHAR path[_MAX_PATH]={0};
            TCHAR drive[_MAX_DRIVE]={0};
            TCHAR dir[_MAX_DIR]={0};
            TCHAR fileName[_MAX_FNAME]={0};
            TCHAR ext[_MAX_EXT]={0};

            _tsplitpath(alert.Command,drive,dir,fileName,ext);
            _tmakepath(path,drive, dir, 0,0);

			ShellExecute(0,0,alert.Command,alert.CommandArgs,path,SW_SHOWDEFAULT);			
		}

		TCHAR text[_MAX_PATH];		

		if (_tcslen(alert.Text) > 0)
			_stprintf(text,TEXT("%02d:%02d:%02d - %02d.%02d.%d: %s - %s"),alert.Time.wHour, alert.Time.wMinute, alert.Time.wSecond, alert.Time.wDay, alert.Time.wMonth, alert.Time.wYear, alert.Text,Translate(TEXT("Scheduler")).c_str());
		else
			_stprintf(text,TEXT("%02d:%02d:%02d - %02d.%02d.%d - %s"),alert.Time.wHour, alert.Time.wMinute, alert.Time.wSecond, alert.Time.wDay, alert.Time.wMonth, alert.Time.wYear,Translate(TEXT("Scheduler")).c_str());

		ShowAlertWindow(text);
	}
}

void Scheduler::InitSetAlertDialog(HWND hWndDlg)
{
    RECT rect;
    GetWindowRect(hWndDlg,&rect);
    SIZE windowSize = {rect.right-rect.left,rect.bottom-rect.top};

    HICON icon = LoadIcon(hInstance,MAKEINTRESOURCE(IDI_ICON1));
    SendMessage(hWndDlg,WM_SETICON,ICON_SMALL,(LPARAM)icon);
    SendMessage(hWndDlg,WM_SETICON,ICON_BIG,(LPARAM)icon);

    SetWindowText(hWndDlg, Translate(3, TEXT("Add Alert"), TEXT("# - "), TEXT("Scheduler")).c_str());

    SetDlgItemText(hWndDlg, IDC_STATIC_DATETIME, Translate(2, TEXT("DateTime"), TEXT("#:")).c_str());
    SetDlgItemText(hWndDlg, IDC_STATIC_TEXT, Translate(2, TEXT("Text"), TEXT("#:")).c_str());
    SetDlgItemText(hWndDlg, IDC_STATIC_COMMAND, Translate(2, TEXT("Command"), TEXT("#:")).c_str());
    SetDlgItemText(hWndDlg, IDC_STATIC_ARGS, Translate(2, TEXT("Arguments"), TEXT("#:")).c_str());
    SetDlgItemText(hWndDlg, IDOK, Translate(TEXT("OK")).c_str());
    SetDlgItemText(hWndDlg, IDC_CHECK_SOUND, Translate(2, TEXT("Play Sound"), TEXT("#:")).c_str());

    DateTime_SetFormat(GetDlgItem(hWndDlg,IDC_DATETIMEPICKER1),TEXT("HH:mm:ss' - 'dd.MM.yyyy"));

    Settings settings;
    Settings::Data data = {0};
    data.SchedulerData.LastSoundActive=true;
    settings.Load(&data);

    SetDlgItemText(hWndDlg,IDC_EDIT_SOUNDFILE,data.SchedulerData.LastSoundFile);
    Button_SetCheck(GetDlgItem(hWndDlg,IDC_CHECK_SOUND),data.SchedulerData.LastSoundActive?BST_CHECKED:BST_UNCHECKED);
    Edit_Enable(GetDlgItem(hWndDlg,IDC_EDIT_SOUNDFILE),data.SchedulerData.LastSoundActive?TRUE:FALSE);		

    time_t now=time(0);

    tm timeInfo;
    localtime_s(&timeInfo,&now);

    //solve the DST problem
    if (timeInfo.tm_isdst > 0)
    {
        timeInfo.tm_hour++;
        now = mktime(&timeInfo);
    }

    double diff = -1;

    diff=difftime(now,alert.time_t);

    if (alert.time_t && diff < 0)
    {
        DateTime_SetSystemtime(GetDlgItem(hWndDlg,IDC_DATETIMEPICKER1), GDT_VALID, &alert.Time);

        SetDlgItemText(hWndDlg,IDC_EDIT_TEXT,alert.Text);
        SetDlgItemText(hWndDlg,IDC_EDIT_COMMAND,alert.Command);
        SetDlgItemText(hWndDlg,IDC_EDIT_ARGS,alert.CommandArgs);
        SetDlgItemText(hWndDlg,IDC_EDIT_SOUNDFILE,alert.SoundFile);
    }

    POINT pos=Utilities::CenterScreen(windowSize);
    SetWindowPos(hWndDlg,HWND_TOPMOST,pos.x,pos.y,0,0,SWP_NOSIZE);
    SetForegroundWindow(hWndDlg);
}

HRESULT Scheduler::ShowOpenFileDialog(HWND hwnd, LPTSTR * fileName, bool waveFiles)
{
    HRESULT hr = CoInitialize(0);

    if (SUCCEEDED(hr))
    {
        IFileDialog *fileDialog;
        
        hr = CoCreateInstance(CLSID_FileOpenDialog, 
                                      0, 
                                      CLSCTX_INPROC_SERVER, 
                                      IID_PPV_ARGS(&fileDialog));
        
        if (SUCCEEDED(hr))
        {
            if (waveFiles)
            {
                COMDLG_FILTERSPEC aFileTypes[] = {
                    { L"Wave files", L"*.wav" }
                };
                fileDialog->SetFileTypes(_countof(aFileTypes), aFileTypes);
            }

            hr = fileDialog->Show(hwnd);
            
            if (SUCCEEDED(hr))
            {
                IShellItem *result;
                hr = fileDialog->GetResult(&result);

                if (SUCCEEDED(hr))
                {
                    result->GetDisplayName(SIGDN_FILESYSPATH,fileName);
                    result->Release();
                }
            }
            fileDialog->Release();
        }
    }

    CoUninitialize();

    return hr;
}

time_t Scheduler::ConvertSystemTimeToTime_t(SYSTEMTIME systemTime)	
{
    time_t time=0;

    tm timeInfo;
    localtime_s(&timeInfo,&time);
    timeInfo.tm_mday = systemTime.wDay;
    timeInfo.tm_mon=systemTime.wMonth-1;
    timeInfo.tm_year=systemTime.wYear-1900;
    timeInfo.tm_hour=systemTime.wHour;
    timeInfo.tm_min=systemTime.wMinute;
    timeInfo.tm_sec=systemTime.wSecond;

    time = mktime(&timeInfo);

    return time;
}


INT_PTR CALLBACK Scheduler::DialogProc(HWND hWndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    static map<HWND, Scheduler*> dlgInstances;
    switch(uMsg)
    {
    case WM_INITDIALOG:
        {
            dlgInstances[hWndDlg] = (Scheduler*)lParam;
            dlgInstances[hWndDlg]->InitSetAlertDialog(hWndDlg);
            break;
        }
    case WM_DISPLAYCHANGE:
        {
            Utilities::MoveWindowIntoVisibleArea(hWndDlg);
            break;
        }
    case WM_COMMAND:
        {
            if (HIWORD(wParam) == BN_CLICKED)
            {
                switch (LOWORD(wParam))
                {				
                case IDC_BUTTONBROWSE:
                    {
                        LPTSTR fileName;
                        HRESULT hr = dlgInstances[hWndDlg]->ShowOpenFileDialog(hWndDlg,&fileName,false);	

                        if (SUCCEEDED(hr))
                            SetDlgItemText(hWndDlg,IDC_EDIT_COMMAND,fileName);

                        break;
                    }
                case IDC_BUTTONBROWSE2:
                    {
                        LPTSTR fileName;
                        HRESULT hr = dlgInstances[hWndDlg]->ShowOpenFileDialog(hWndDlg,&fileName, true);	

                        if (SUCCEEDED(hr))
                            SetDlgItemText(hWndDlg,IDC_EDIT_SOUNDFILE,fileName);

                        break;
                    }
                case IDC_CHECK_SOUND:
                    {
                        if (Button_GetCheck(GetDlgItem(hWndDlg,IDC_CHECK_SOUND)) == BST_CHECKED)
                        {
                            Button_Enable(GetDlgItem(hWndDlg,IDC_BUTTONBROWSE2), TRUE);
                            Edit_Enable(GetDlgItem(hWndDlg,IDC_EDIT_SOUNDFILE),TRUE);		
                        }
                        else
                        {
                            Button_Enable(GetDlgItem(hWndDlg,IDC_BUTTONBROWSE2), FALSE);
                            Edit_Enable(GetDlgItem(hWndDlg,IDC_EDIT_SOUNDFILE),FALSE);		
                        }

                        break;
                    }				
                case IDOK:
                    {
                        dlgInstances[hWndDlg]->CreateAlert(hWndDlg);
                        //fall through
                    }
                case IDCANCEL:
                    {
                        EndDialog(hWndDlg, wParam);
                        break;
                    }

                }
            }
            break;
        }
    default:
        return FALSE;
    }

    return TRUE;
}

VOID CALLBACK Scheduler::TimerProc(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
    instances[hWnd]->CheckAlert();
}

LRESULT CALLBACK Scheduler::SchedulerAlertWindowWndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{	
    static BOOL dwmEnabled = -1;
    static LPCWSTR text=0;

    if (dwmEnabled == -1)
        DwmIsCompositionEnabled(&dwmEnabled);

    switch (uMsg)
    {
    case WM_CREATE:
        {
            if (!text)
            {
                CREATESTRUCT * cs = (CREATESTRUCT*)lParam;
                text = (LPWSTR)cs->lpCreateParams;
            }

            break;
        }
    case WM_PAINT:
        {
            HDC          hdc;
            PAINTSTRUCT  ps;

            hdc = BeginPaint(hWnd, &ps);
            
            RECT rect={0};
            GetClientRect(hWnd, &rect);
            Utilities::DrawDwmCompatibleText(rect, hdc, text, 26, RGB(0, 0, 0), DT_CENTER|DT_END_ELLIPSIS, FW_NORMAL, 15, 8, 8);

            EndPaint(hWnd, &ps);

            break;
        }
    case WM_DWMCOMPOSITIONCHANGED:
        {
            DwmIsCompositionEnabled(&dwmEnabled);

            if (dwmEnabled)
            {
                SetClassLongPtr(hWnd,GCL_HBRBACKGROUND,(LONG)GetStockObject(BLACK_BRUSH));
                MARGINS margins = {-1,-1,-1,-1};
                DwmExtendFrameIntoClientArea(hWnd,&margins);
            }
            else
            {
                if (GetActiveWindow() == hWnd)
                    SetClassLongPtr(hWnd,GCL_HBRBACKGROUND,(LONG)GetSysColorBrush(COLOR_GRADIENTACTIVECAPTION));
                else
                    SetClassLongPtr(hWnd,GCL_HBRBACKGROUND,(LONG)GetSysColorBrush(COLOR_GRADIENTINACTIVECAPTION));
            }

            break;
        }
    case WM_DISPLAYCHANGE:
        {
            Utilities::MoveWindowIntoVisibleArea(hWnd);
            break;
        }
    //case WM_MOUSEACTIVATE:
    //case WM_NCACTIVATE:
    //case WM_ACTIVATEAPP:
    case WM_ACTIVATE:
        {
            if (!dwmEnabled)
            {
                if (!wParam) //inactivate
                    SetClassLongPtr(hWnd,GCL_HBRBACKGROUND,(LONG)GetSysColorBrush(COLOR_GRADIENTINACTIVECAPTION));
                else
                    SetClassLongPtr(hWnd,GCL_HBRBACKGROUND,(LONG)GetSysColorBrush(COLOR_GRADIENTACTIVECAPTION));
                
                InvalidateRgn(hWnd,0,true);
            }

            break;
        }
    case WM_DESTROY:
        {
            PlaySound(0,0,0);
            return S_OK;
        }
    case WM_CLOSE:
        {				
            DestroyWindow(hWnd);
            return S_OK;
        }
    }

    return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

LRESULT CALLBACK Scheduler::SchedulerHiddenWindowWndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{	
    switch (uMsg)
    {
    case WM_HOTKEY:
        {	
            if (wParam == 1)
                instances[hWnd]->SetAlert();
            break;
        }
    }

    return DefWindowProc(hWnd,uMsg,wParam,lParam);
}